home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkGrab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  46.9 KB  |  1,560 lines

  1. /* 
  2.  * tkGrab.c --
  3.  *
  4.  *    This file provides procedures that implement grabs for Tk.
  5.  *
  6.  * Copyright (c) 1992-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkGrab.c 1.50 96/02/15 18:53:34
  13.  */
  14.  
  15. #include "tkPort.h"
  16. #include "tkInt.h"
  17.  
  18. /*
  19.  * The grab state machine has four states: ungrabbed, button pressed,
  20.  * grabbed, and button pressed while grabbed.  In addition, there are
  21.  * three pieces of grab state information: the current grab window,
  22.  * the current restrict window, and whether the mouse is captured.
  23.  *
  24.  * The current grab window specifies the point in the Tk window
  25.  * heirarchy above which pointer events will not be reported.  Any
  26.  * window within the subtree below the grab window will continue to
  27.  * receive events as normal.  Events outside of the grab tree will be
  28.  * reported to the grab window.
  29.  *
  30.  * If the current restrict window is set, then all pointer events will
  31.  * be reported only to the restrict window.  The restrict window is
  32.  * normally set during an automatic button grab.
  33.  *
  34.  * The mouse capture state specifies whether the window system will
  35.  * report mouse events outside of any Tk toplevels.  This is set
  36.  * during a global grab or an automatic button grab.
  37.  *
  38.  * The transitions between different states is given in the following
  39.  * table:
  40.  * 
  41.  * Event\State    U    B    G    GB
  42.  * -----------    --    --    --    --
  43.  * FirstPress    B    B    GB    GB
  44.  * Press    B    B    G    GB
  45.  * Release    U    B    G    GB
  46.  * LastRelease    U    U    G    G
  47.  * Grab        G    G    G    G
  48.  * Ungrab    U    B    U    U
  49.  *
  50.  * Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button
  51.  *
  52.  * In addition, the following conditions are always true:
  53.  *
  54.  * State\Variable    Grab         Restrict         Capture
  55.  * --------------    ----         --------         -------
  56.  * Ungrabbed         0        0        0
  57.  * Button         0        1        1
  58.  * Grabbed         1        0        b/g
  59.  * Grab and Button     1        1        1
  60.  *
  61.  * Note: 0 means variable is set to NULL, 1 means variable is set to
  62.  * some window, b/g means the variable is set to a window if a button
  63.  * is currently down or a global grab is in effect.
  64.  *
  65.  * The final complication to all of this is enter and leave events.
  66.  * In order to correctly handle all of the various cases, Tk cannot
  67.  * rely on X enter/leave events in all situations.  The following
  68.  * describes the correct sequence of enter and leave events that
  69.  * should be observed by Tk scripts:
  70.  *
  71.  * Event(state)        Enter/Leave From -> To
  72.  * ------------        ----------------------
  73.  * LastRelease(B | GB): restrict window -> anc(grab window, event window)
  74.  * Grab(U | B):     event window -> anc(grab window, event window)
  75.  * Grab(G):        anc(old grab window, event window) ->
  76.  *                 anc(new grab window, event window)
  77.  * Grab(GB):        restrict window -> anc(new grab window, event window)
  78.  * Ungrab(G):        anc(grab window, event window) -> event window
  79.  * Ungrab(GB):        restrict window -> event window
  80.  *
  81.  * Note: anc(x,y) returns the least ancestor of y that is in the tree
  82.  * of x, terminating at toplevels.
  83.  */
  84.  
  85. /*
  86.  * The following structure is used to pass information to 
  87.  * GrabRestrictProc from EatGrabEvents.
  88.  */
  89.  
  90. typedef struct {
  91.     Display *display;        /* Display from which to discard events. */
  92.     unsigned int serial;    /* Serial number with which to compare. */
  93. } GrabInfo;
  94.  
  95. /*
  96.  * Bit definitions for grabFlags field of TkDisplay structures:
  97.  *
  98.  * GRAB_GLOBAL            1 means this is a global grab (we grabbed via
  99.  *                the server so all applications are locked out).
  100.  *                0 means this is a local grab that affects
  101.  *                only this application.
  102.  * GRAB_TEMP_GLOBAL        1 means we've temporarily grabbed via the
  103.  *                server because a button is down and we want
  104.  *                to make sure that we get the button-up
  105.  *                event.  The grab will be released when the
  106.  *                last mouse button goes up.
  107.  */
  108.  
  109. #define GRAB_GLOBAL        1
  110. #define GRAB_TEMP_GLOBAL    4
  111.  
  112. /*
  113.  * The following structure is a Tcl_Event that triggers a change in
  114.  * the grabWinPtr field of a display.  This event guarantees that
  115.  * the change occurs in the proper order relative to enter and leave
  116.  * events.
  117.  */
  118.  
  119. typedef struct NewGrabWinEvent {
  120.     Tcl_Event header;        /* Standard information for all Tcl events. */
  121.     TkDisplay *dispPtr;        /* Display whose grab window is to change. */
  122.     Window grabWindow;        /* New grab window for display.  This is
  123.                  * recorded instead of a (TkWindow *) because
  124.                  * it will allow us to detect cases where
  125.                  * the window is destroyed before this event
  126.                  * is processed. */
  127. } NewGrabWinEvent;
  128.  
  129. /*
  130.  * The following magic value is stored in the "send_event" field of
  131.  * EnterNotify and LeaveNotify events that are generated in this
  132.  * file.  This allows us to separate "real" events coming from the
  133.  * server from those that we generated.
  134.  */
  135.  
  136. #define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac)
  137.  
  138. /*
  139.  * Mask that selects any of the state bits corresponding to buttons,
  140.  * plus masks that select individual buttons' bits:
  141.  */
  142.  
  143. #define ALL_BUTTONS \
  144.     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
  145. static unsigned int buttonStates[] = {
  146.     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
  147. };
  148.  
  149. /*
  150.  * Forward declarations for procedures declared later in this file:
  151.  */
  152.  
  153. static void        EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr,
  154.                 unsigned int serial));
  155. static TkWindow *    FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1,
  156.                 TkWindow *winPtr2, int *countPtr1,
  157.                 int *countPtr2));
  158. static Tk_RestrictAction GrabRestrictProc _ANSI_ARGS_((ClientData arg,
  159.                 XEvent *eventPtr));
  160. static int        GrabWinEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  161.                 int flags));
  162. static void        MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
  163.                 TkWindow *destPtr, int mode, int leaveEvents,
  164.                 int EnterEvents));
  165. static void        QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr,
  166.                 TkWindow *grabWinPtr));
  167. static void        ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr));
  168.  
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Tk_GrabCmd --
  173.  *
  174.  *    This procedure is invoked to process the "grab" Tcl command.
  175.  *    See the user documentation for details on what it does.
  176.  *
  177.  * Results:
  178.  *    A standard Tcl result.
  179.  *
  180.  * Side effects:
  181.  *    See the user documentation.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185.  
  186.     /* ARGSUSED */
  187. int
  188. Tk_GrabCmd(clientData, interp, argc, argv)
  189.     ClientData clientData;    /* Main window associated with
  190.                  * interpreter. */
  191.     Tcl_Interp *interp;        /* Current interpreter. */
  192.     int argc;            /* Number of arguments. */
  193.     char **argv;        /* Argument strings. */
  194. {
  195.     int globalGrab, c;
  196.     Tk_Window tkwin;
  197.     TkDisplay *dispPtr;
  198.     size_t length;
  199.  
  200.     if (argc < 2) {
  201.     badArgs:
  202.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  203. #ifdef STk_CODE
  204.         argv[0], " ?:global? window\" or \"", argv[0],
  205. #else
  206.         argv[0], " ?-global? window\" or \"", argv[0],
  207. #endif
  208.         " option ?arg arg ...?\"", (char *) NULL);
  209.     return TCL_ERROR;
  210.     }
  211.     c = argv[1][0];
  212.     length = strlen(argv[1]);
  213.     if (c == '.') {
  214.     if (argc != 2) {
  215.         goto badArgs;
  216.     }
  217.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  218.     if (tkwin == NULL) {
  219.         return TCL_ERROR;
  220.     }
  221.     return Tk_Grab(interp, tkwin, 0);
  222.     } else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0)
  223.         && (length >= 2)) {
  224.     if (argc != 3) {
  225.         goto badArgs;
  226.     }
  227.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  228.     if (tkwin == NULL) {
  229.         return TCL_ERROR;
  230.     }
  231.     return Tk_Grab(interp, tkwin, 1);
  232.     } else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) {
  233.     if (argc > 3) {
  234.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  235.             argv[0], " current ?window?\"", (char *) NULL);
  236.         return TCL_ERROR;
  237.     }
  238.     if (argc == 3) {
  239.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  240.         if (tkwin == NULL) {
  241.         return TCL_ERROR;
  242.         }
  243.         dispPtr = ((TkWindow *) tkwin)->dispPtr;
  244.         if (dispPtr->eventualGrabWinPtr != NULL) {
  245. #ifdef STk_CODE
  246.         STk_sharp_dot_result(interp, dispPtr->eventualGrabWinPtr->pathName);
  247. #else
  248.         interp->result = dispPtr->eventualGrabWinPtr->pathName;
  249. #endif
  250.         }
  251. #ifdef STk_CODE
  252.         else
  253.           interp->result = "#f";
  254. #endif
  255.     } else {
  256. #ifdef STk_CODE
  257.         Tcl_AppendResult(interp, "(", (char *) NULL);
  258. #endif
  259.         for (dispPtr = tkDisplayList; dispPtr != NULL;
  260.             dispPtr = dispPtr->nextPtr) {
  261.         if (dispPtr->eventualGrabWinPtr != NULL) {
  262. #ifdef STk_CODE
  263.           Tcl_AppendResult(interp, " #.",
  264.                    dispPtr->eventualGrabWinPtr->pathName, 
  265.                    (char *) NULL);
  266. #else
  267.             Tcl_AppendElement(interp,
  268.                 dispPtr->eventualGrabWinPtr->pathName);
  269. #endif
  270.         }
  271.         }
  272. #ifdef STk_CODE
  273.         Tcl_AppendResult(interp, ")", (char *) NULL);
  274. #endif
  275.     }
  276.     return TCL_OK;
  277.     } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
  278.     if (argc != 3) {
  279.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  280.             argv[0], " release window\"", (char *) NULL);
  281.         return TCL_ERROR;
  282.     }
  283.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  284.     if (tkwin == NULL) {
  285.         Tcl_ResetResult(interp);
  286.     } else {
  287.         Tk_Ungrab(tkwin);
  288.     }
  289.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)
  290.         && (length >= 2)) {
  291.     if ((argc != 3) && (argc != 4)) {
  292.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  293. #ifdef STk_CODE
  294.             argv[0], " set ?:global? window\"", (char *) NULL);
  295. #else
  296.             argv[0], " set ?-global? window\"", (char *) NULL);
  297. #endif
  298.         return TCL_ERROR;
  299.     }
  300.     if (argc == 3) {
  301.         globalGrab = 0;
  302.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  303.     } else {
  304.         globalGrab = 1;
  305.         length = strlen(argv[2]);
  306.         if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) {
  307.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  308. #ifdef STk_CODE
  309.             "\": must be \"", argv[0], " set ?:global? window\"",
  310. #else
  311.             "\": must be \"", argv[0], " set ?-global? window\"",
  312. #endif
  313.             (char *) NULL);
  314.         return TCL_ERROR;
  315.         }
  316.         tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData);
  317.     }
  318.     if (tkwin == NULL) {
  319.         return TCL_ERROR;
  320.     }
  321.     return Tk_Grab(interp, tkwin, globalGrab);
  322.     } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)
  323.         && (length >= 2)) {
  324.     TkWindow *winPtr;
  325.  
  326.     if (argc != 3) {
  327.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  328.             argv[0], " status window\"", (char *) NULL);
  329.         return TCL_ERROR;
  330.     }
  331.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2],
  332.         (Tk_Window) clientData);
  333.     if (winPtr == NULL) {
  334.         return TCL_ERROR;
  335.     }
  336.     dispPtr = winPtr->dispPtr;
  337. #ifdef STk_CODE
  338.     if (dispPtr->eventualGrabWinPtr != winPtr) {
  339.         interp->result = "\"none\"";
  340.     } else if (dispPtr->grabFlags & GRAB_GLOBAL) {
  341.         interp->result = "\"global\"";
  342.     } else {
  343.         interp->result = "\"local\"";
  344.     }
  345. #else
  346.     if (dispPtr->eventualGrabWinPtr != winPtr) {
  347.         interp->result = "none";
  348.     } else if (dispPtr->grabFlags & GRAB_GLOBAL) {
  349.         interp->result = "global";
  350.     } else {
  351.         interp->result = "local";
  352.     }
  353. #endif
  354.     } else {
  355.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  356.         "\": must be current, release, set, or status",
  357.         (char *) NULL);
  358.     return TCL_ERROR;
  359.     }
  360.     return TCL_OK;
  361. }
  362.  
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * Tk_Grab --
  367.  *
  368.  *    Grabs the pointer and keyboard, so that mouse-related events are
  369.  *    only reported relative to a given window and its descendants.
  370.  *
  371.  * Results:
  372.  *    A standard Tcl result is returned.  TCL_OK is the normal return
  373.  *    value;  if the grab could not be set then TCL_ERROR is returned
  374.  *    and interp->result will hold an error message.
  375.  *
  376.  * Side effects:
  377.  *    Once this call completes successfully, no window outside the
  378.  *    tree rooted at tkwin will receive pointer- or keyboard-related
  379.  *    events until the next call to Tk_Ungrab.  If a previous grab was
  380.  *    in effect within this application, then it is replaced with a new
  381.  *    one.
  382.  *
  383.  *----------------------------------------------------------------------
  384.  */
  385.  
  386. int
  387. Tk_Grab(interp, tkwin, grabGlobal)
  388.     Tcl_Interp *interp;            /* Used for error reporting. */
  389.     Tk_Window tkwin;            /* Window on whose behalf the pointer
  390.                      * is to be grabbed. */
  391.     int grabGlobal;            /* Non-zero means issue a grab to the
  392.                      * server so that no other application
  393.                      * gets mouse or keyboard events.
  394.                      * Zero means the grab only applies
  395.                      * within this application. */
  396. {
  397.     int grabResult;
  398.     TkWindow *winPtr = (TkWindow *) tkwin;
  399.     TkDisplay *dispPtr = winPtr->dispPtr;
  400.     TkWindow *winPtr2;
  401.     unsigned int serial;
  402.  
  403.     ReleaseButtonGrab(dispPtr);
  404.     if (dispPtr->eventualGrabWinPtr != NULL) {
  405.     if ((dispPtr->eventualGrabWinPtr == winPtr)
  406.         && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
  407.         return TCL_OK;
  408.     }
  409.     if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
  410.         alreadyGrabbed:
  411.         interp->result = "grab failed: another application has grab";
  412.         return TCL_ERROR;
  413.     }
  414.     Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
  415.     }
  416.  
  417.     Tk_MakeWindowExist(tkwin);
  418.     if (!grabGlobal) {
  419.     Window dummy1, dummy2;
  420.     int dummy3, dummy4, dummy5, dummy6;
  421.     unsigned int state;
  422.  
  423.     /*
  424.      * Local grab.  However, if any mouse buttons are down, turn
  425.      * it into a global grab temporarily, until the last button
  426.      * goes up.  This does two things: (a) it makes sure that we
  427.      * see the button-up event;  and (b) it allows us to track mouse
  428.      * motion among all of the windows of this application.
  429.      */
  430.  
  431.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  432.     XQueryPointer(dispPtr->display, winPtr->window, &dummy1,
  433.         &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state);
  434.     if ((state & ALL_BUTTONS) != 0) {
  435.         dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  436.         goto setGlobalGrab;
  437.     }
  438.     } else {
  439.     dispPtr->grabFlags |= GRAB_GLOBAL;
  440.     setGlobalGrab:
  441.  
  442.     /*
  443.      * Tricky point: must ungrab before grabbing.  This is needed
  444.      * in case there is a button auto-grab already in effect.  If
  445.      * there is, and the mouse has moved to a different window, X
  446.      * won't generate enter and leave events to move the mouse if
  447.      * we grab without ungrabbing.
  448.      */
  449.  
  450.     XUngrabPointer(dispPtr->display, CurrentTime);
  451.     serial = NextRequest(dispPtr->display);
  452.     grabResult = XGrabPointer(dispPtr->display, winPtr->window,
  453.         True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
  454.         |PointerMotionMask, GrabModeAsync, GrabModeAsync, None,
  455.         None, CurrentTime);
  456.     if (grabResult != 0) {
  457.         grabError:
  458.         if (grabResult == GrabNotViewable) {
  459.         interp->result = "grab failed: window not viewable";
  460.         } else if (grabResult == AlreadyGrabbed) {
  461.         goto alreadyGrabbed;
  462.         } else if (grabResult == GrabFrozen) {
  463.         interp->result = "grab failed: keyboard or pointer frozen";
  464.         } else if (grabResult == GrabInvalidTime) {
  465.         interp->result = "grab failed: invalid time";
  466.         } else {
  467.         char msg[100];
  468.     
  469.         sprintf(msg, "grab failed for unknown reason (code %d)",
  470.             grabResult);
  471.         Tcl_AppendResult(interp, msg, (char *) NULL);
  472.         }
  473.         return TCL_ERROR;
  474.     }
  475.     grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
  476.         False, GrabModeAsync, GrabModeAsync, CurrentTime);
  477.     if (grabResult != 0) {
  478.         XUngrabPointer(dispPtr->display, CurrentTime);
  479.         goto grabError;
  480.     }
  481.  
  482.     /*
  483.      * Eat up any grab-related events generated by the server for the
  484.      * grab.  There are several reasons for doing this:
  485.      *
  486.      * 1. We have to synthesize the events for local grabs anyway, since
  487.      *    the server doesn't participate in them.
  488.      * 2. The server doesn't always generate the right events for global
  489.      *    grabs (e.g. it generates events even if the current window is
  490.      *    in the grab tree, which we don't want).
  491.      * 3. We want all the grab-related events to be processed immediately
  492.      *    (before other events that are already queued); events coming
  493.      *    from the server will be in the wrong place, but events we
  494.      *    synthesize here will go to the front of the queue.
  495.      */
  496.  
  497.     EatGrabEvents(dispPtr, serial);
  498.     }
  499.  
  500.     /*
  501.      * Synthesize leave events to move the pointer from its current window
  502.      * up to the lowest ancestor that it has in common with the grab window.
  503.      * However, only do this if the pointer is outside the grab window's
  504.      * subtree but inside the grab window's application.
  505.      */
  506.  
  507.     if ((dispPtr->serverWinPtr != NULL)
  508.         && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) {
  509.     for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) {
  510.         if (winPtr2 == winPtr) {
  511.         break;
  512.         }
  513.         if (winPtr2 == NULL) {
  514.         MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0);
  515.         break;
  516.         }
  517.     }
  518.     }
  519.     QueueGrabWindowChange(dispPtr, winPtr);
  520.     return TCL_OK;
  521. }
  522.  
  523. /*
  524.  *----------------------------------------------------------------------
  525.  *
  526.  * Tk_Ungrab --
  527.  *
  528.  *    Releases a grab on the mouse pointer and keyboard, if there
  529.  *    is one set on the specified window.
  530.  *
  531.  * Results:
  532.  *    None.
  533.  *
  534.  * Side effects:
  535.  *    Pointer and keyboard events will start being delivered to other
  536.  *    windows again.
  537.  *
  538.  *----------------------------------------------------------------------
  539.  */
  540.  
  541. void
  542. Tk_Ungrab(tkwin)
  543.     Tk_Window tkwin;            /* Window whose grab should be
  544.                      * released. */
  545. {
  546.     TkDisplay *dispPtr;
  547.     TkWindow *grabWinPtr, *winPtr;
  548.     unsigned int serial;
  549.  
  550.     grabWinPtr = (TkWindow *) tkwin;
  551.     dispPtr = grabWinPtr->dispPtr;
  552.     if (grabWinPtr != dispPtr->eventualGrabWinPtr) {
  553.     return;
  554.     }
  555.     ReleaseButtonGrab(dispPtr);
  556.     QueueGrabWindowChange(dispPtr, (TkWindow *) NULL);
  557.     if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) {
  558.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  559.     serial = NextRequest(dispPtr->display);
  560.     XUngrabPointer(dispPtr->display, CurrentTime);
  561.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  562.     EatGrabEvents(dispPtr, serial);
  563.     }
  564.  
  565.     /*
  566.      * Generate events to move the pointer back to the window where it
  567.      * really is.  Some notes:
  568.      * 1. As with grabs, only do this if the "real" window is not a
  569.      *    descendant of the grab window, since in this case the pointer
  570.      *    is already where it's supposed to be.
  571.      * 2. If the "real" window is in some other application then don't
  572.      *    generate any events at all, since everything's already been
  573.      *    reported correctly.
  574.      * 3. Only generate enter events.  Don't generate leave events,
  575.      *    because we never told the lower-level windows that they
  576.      *    had the pointer in the first place.
  577.      */
  578.  
  579.     for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) {
  580.     if (winPtr == grabWinPtr) {
  581.         break;
  582.     }
  583.     if (winPtr == NULL) {
  584.         if ((dispPtr->serverWinPtr == NULL) ||
  585.             (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) {
  586.         MovePointer2(grabWinPtr, dispPtr->serverWinPtr,
  587.             NotifyUngrab, 0, 1);
  588.         }
  589.         break;
  590.     }
  591.     }
  592. }
  593.  
  594. /*
  595.  *----------------------------------------------------------------------
  596.  *
  597.  * ReleaseButtonGrab --
  598.  *
  599.  *    This procedure is called to release a simulated button grab, if
  600.  *    there is one in effect.  A button grab is present whenever
  601.  *    dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL
  602.  *    flag is set.
  603.  *
  604.  * Results:
  605.  *    None.
  606.  *
  607.  * Side effects:
  608.  *    DispPtr->buttonWinPtr is reset to NULL, and enter and leave
  609.  *    events are generated if necessary to move the pointer from
  610.  *    the button grab window to its current window.
  611.  *
  612.  *----------------------------------------------------------------------
  613.  */
  614.  
  615. static void
  616. ReleaseButtonGrab(dispPtr)
  617.     register TkDisplay *dispPtr;    /* Display whose button grab is to be
  618.                      * released. */
  619. {
  620.     unsigned int serial;
  621.  
  622.     if (dispPtr->buttonWinPtr != NULL) {
  623.     if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) {
  624.         MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr,
  625.             NotifyUngrab, 1, 1);
  626.     }
  627.     dispPtr->buttonWinPtr = NULL;
  628.     }
  629.     if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) {
  630.     dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL;
  631.     serial = NextRequest(dispPtr->display);
  632.     XUngrabPointer(dispPtr->display, CurrentTime);
  633.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  634.     EatGrabEvents(dispPtr, serial);
  635.     }
  636. }
  637.  
  638. /*
  639.  *----------------------------------------------------------------------
  640.  *
  641.  * TkPointerEvent --
  642.  *
  643.  *    This procedure is called for each pointer-related event, before
  644.  *    the event has been processed.  It does various things to make
  645.  *    grabs work correctly.
  646.  *
  647.  * Results:
  648.  *    If the return value is 1 it means the event should be processed
  649.  *    (event handlers should be invoked).  If the return value is 0
  650.  *    it means the event should be ignored in order to make grabs
  651.  *    work correctly.  In some cases this procedure modifies the event.
  652.  *
  653.  * Side effects:
  654.  *    Grab state information may be updated.  New events may also be
  655.  *    pushed back onto the event queue to replace or augment the
  656.  *    one passed in here.
  657.  *
  658.  *----------------------------------------------------------------------
  659.  */
  660.  
  661. int
  662. TkPointerEvent(eventPtr, winPtr)
  663.     register XEvent *eventPtr;        /* Pointer to the event. */
  664.     TkWindow *winPtr;            /* Tk's information for window
  665.                      * where event was reported. */
  666. {
  667.     register TkWindow *winPtr2;
  668.     TkDisplay *dispPtr = winPtr->dispPtr;
  669.     unsigned int serial;
  670.     int outsideGrabTree = 0;
  671.     int ancestorOfGrab = 0;
  672.     int appGrabbed = 0;            /* Non-zero means event is being
  673.                      * reported to an application that is
  674.                      * affected by the grab. */
  675.  
  676.     /*
  677.      * Collect information about the grab (if any).
  678.      */
  679.  
  680.     switch (TkGrabState(winPtr)) {
  681.     case TK_GRAB_IN_TREE:
  682.         appGrabbed = 1;
  683.         break;
  684.     case TK_GRAB_ANCESTOR:
  685.         appGrabbed = 1;
  686.         outsideGrabTree = 1;
  687.         ancestorOfGrab = 1;
  688.         break;
  689.     case TK_GRAB_EXCLUDED:
  690.         appGrabbed = 1;
  691.         outsideGrabTree = 1;
  692.         break;
  693.     }
  694.  
  695.     if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
  696.     /*
  697.      * Keep track of what window the mouse is *really* over.
  698.      * Any events that we generate have a special send_event value,
  699.      * which is detected below and used to ignore the event for
  700.      * purposes of setting serverWinPtr.
  701.      */
  702.  
  703.     if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) {
  704.         if ((eventPtr->type == LeaveNotify) &&
  705.             (winPtr->flags & TK_TOP_LEVEL)) {
  706.         dispPtr->serverWinPtr = NULL;
  707.         } else {
  708.         dispPtr->serverWinPtr = winPtr;
  709.         }
  710.     }
  711.  
  712.     /*
  713.      * When a grab is active, X continues to report enter and leave
  714.      * events for windows outside the tree of the grab window:
  715.      * 1. Detect these events and ignore them except for
  716.      *    windows above the grab window.
  717.      * 2. Allow Enter and Leave events to pass through the
  718.      *    windows above the grab window, but never let them
  719.      *    end up with the pointer *in* one of those windows.
  720.      */
  721.  
  722.     if (dispPtr->grabWinPtr != NULL) {
  723.         if (outsideGrabTree && appGrabbed) {
  724.         if (!ancestorOfGrab) {
  725.             return 0;
  726.         }
  727.         switch (eventPtr->xcrossing.detail) {
  728.             case NotifyInferior:
  729.             return 0;
  730.             case NotifyAncestor:
  731.             eventPtr->xcrossing.detail = NotifyVirtual;
  732.             break;
  733.             case NotifyNonlinear:
  734.             eventPtr->xcrossing.detail = NotifyNonlinearVirtual;
  735.             break;
  736.         }
  737.         }
  738.  
  739.         /*
  740.          * Make buttons have the same grab-like behavior inside a grab
  741.          * as they do outside a grab:  do this by ignoring enter and
  742.          * leave events except for the window in which the button was
  743.          * pressed.
  744.          */
  745.  
  746.         if ((dispPtr->buttonWinPtr != NULL)
  747.             && (winPtr != dispPtr->buttonWinPtr)) {
  748.         return 0;
  749.         }
  750.     }
  751.     return 1;
  752.     }
  753.  
  754.     if (!appGrabbed) {
  755.     return 1;
  756.     }
  757.  
  758.     if (eventPtr->type == MotionNotify) {
  759.     /*
  760.      * When grabs are active, X reports motion events relative to the
  761.      * window under the pointer.  Instead, it should report the events
  762.      * relative to the window the button went down in, if there is a
  763.      * button down.  Otherwise, if the pointer window is outside the
  764.      * subtree of the grab window, the events should be reported
  765.      * relative to the grab window.  Otherwise, the event should be
  766.      * reported to the pointer window.
  767.      */
  768.  
  769.     winPtr2 = winPtr;
  770.     if (dispPtr->buttonWinPtr != NULL) {
  771.         winPtr2 = dispPtr->buttonWinPtr;
  772.     } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
  773.         winPtr2 = dispPtr->grabWinPtr;
  774.     }
  775.     if (winPtr2 != winPtr) {
  776.         TkChangeEventWindow(eventPtr, winPtr2);
  777.         Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  778.         return 0;
  779.     }
  780.     return 1;
  781.     }
  782.  
  783.     /*
  784.      * Process ButtonPress and ButtonRelease events:
  785.      * 1. Keep track of whether a button is down and what window it
  786.      *    went down in.
  787.      * 2. If the first button goes down outside the grab tree, pretend
  788.      *    it went down in the grab window.  Note: it's important to
  789.      *    redirect events to the grab window like this in order to make
  790.      *    things like menus work, where button presses outside the
  791.      *    grabbed menu need to be seen.  An application can always
  792.      *    ignore the events if they occur outside its window.
  793.      * 3. If a button press or release occurs outside the window where
  794.      *    the first button was pressed, retarget the event so it's reported
  795.      *    to the window where the first button was pressed.
  796.      * 4. If the last button is released in a window different than where
  797.      *    the first button was pressed, generate Enter/Leave events to
  798.      *    move the mouse from the button window to its current window.
  799.      * 5. If the grab is set at a time when a button is already down, or
  800.      *    if the window where the button was pressed was deleted, then
  801.      *    dispPtr->buttonWinPtr will stay NULL.  Just forget about the
  802.      *    auto-grab for the button press;  events will go to whatever
  803.      *    window contains the pointer.  If this window isn't in the grab
  804.      *    tree then redirect events to the grab window.
  805.      * 6. When a button is pressed during a local grab, the X server sets
  806.      *    a grab of its own, since it doesn't even know about our local
  807.      *    grab.  This causes enter and leave events no longer to be
  808.      *    generated in the same way as for global grabs.  To eliminate this
  809.      *    problem, set a temporary global grab when the first button goes
  810.      *    down and release it when the last button comes up.
  811.      */
  812.  
  813.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  814.     winPtr2 = dispPtr->buttonWinPtr;
  815.     if (winPtr2 == NULL) {
  816.         if (outsideGrabTree) {
  817.         winPtr2 = dispPtr->grabWinPtr;            /* Note 5. */
  818.         } else {
  819.         winPtr2 = winPtr;                /* Note 5. */
  820.         }
  821.     }
  822.     if (eventPtr->type == ButtonPress) {
  823.         if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
  824.         if (outsideGrabTree) {
  825.             TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr);
  826.             Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  827.             return 0;                    /* Note 2. */
  828.         }
  829.         if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {    /* Note 6. */
  830.             serial = NextRequest(dispPtr->display);
  831.             if (XGrabPointer(dispPtr->display,
  832.                 dispPtr->grabWinPtr->window, True,
  833.                 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
  834.                 GrabModeAsync, GrabModeAsync, None, None,
  835.                 CurrentTime) == 0) {
  836.             EatGrabEvents(dispPtr, serial);
  837.             if (XGrabKeyboard(dispPtr->display, winPtr->window,
  838.                 False, GrabModeAsync, GrabModeAsync,
  839.                 CurrentTime) == 0) {
  840.                 dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  841.             } else {
  842.                 XUngrabPointer(dispPtr->display, CurrentTime);
  843.             }
  844.             }
  845.         }
  846.         dispPtr->buttonWinPtr = winPtr;
  847.         return 1;
  848.         }
  849.     } else {
  850.         if ((eventPtr->xbutton.state & ALL_BUTTONS)
  851.             == buttonStates[eventPtr->xbutton.button - Button1]) {
  852.         ReleaseButtonGrab(dispPtr);            /* Note 4. */
  853.         }
  854.     }
  855.     if (winPtr2 != winPtr) {
  856.         TkChangeEventWindow(eventPtr, winPtr2);
  857.         Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  858.         return 0;                        /* Note 3. */
  859.     }
  860.     }
  861.  
  862.     return 1;
  863. }
  864.  
  865. /*
  866.  *----------------------------------------------------------------------
  867.  *
  868.  * TkChangeEventWindow --
  869.  *
  870.  *    Given an event and a new window to which the event should be
  871.  *    retargeted, modify fields of the event so that the event is
  872.  *    properly retargeted to the new window.
  873.  *
  874.  * Results:
  875.  *    The following fields of eventPtr are modified:  window,
  876.  *    subwindow, x, y, same_screen.
  877.  *
  878.  * Side effects:
  879.  *    None.
  880.  *
  881.  *----------------------------------------------------------------------
  882.  */
  883.  
  884. void
  885. TkChangeEventWindow(eventPtr, winPtr)
  886.     register XEvent *eventPtr;    /* Event to retarget.  Must have
  887.                  * type ButtonPress, ButtonRelease, KeyPress,
  888.                  * KeyRelease, MotionNotify, EnterNotify,
  889.                  * or LeaveNotify. */
  890.     TkWindow *winPtr;        /* New target window for event. */
  891. {
  892.     int x, y, sameScreen, bd;
  893.     register TkWindow *childPtr;
  894.  
  895.     eventPtr->xmotion.window = Tk_WindowId(winPtr);
  896.     if (eventPtr->xmotion.root ==
  897.         RootWindow(winPtr->display, winPtr->screenNum)) {
  898.     Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
  899.     eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
  900.     eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
  901.     eventPtr->xmotion.subwindow = None;
  902.     for (childPtr = winPtr->childList; childPtr != NULL;
  903.         childPtr = childPtr->nextPtr) {
  904.         if (childPtr->flags & TK_TOP_LEVEL) {
  905.         continue;
  906.         }
  907.         x = eventPtr->xmotion.x - childPtr->changes.x;
  908.         y = eventPtr->xmotion.y - childPtr->changes.y;
  909.         bd = childPtr->changes.border_width;
  910.         if ((x >= -bd) && (y >= -bd)
  911.             && (x < (childPtr->changes.width + bd))
  912.             && (y < (childPtr->changes.height + bd))) {
  913.         eventPtr->xmotion.subwindow = childPtr->window;
  914.         }
  915.     }
  916.     sameScreen = 1;
  917.     } else {
  918.     eventPtr->xmotion.x = 0;
  919.     eventPtr->xmotion.y = 0;
  920.     eventPtr->xmotion.subwindow = None;
  921.     sameScreen = 0;
  922.     }
  923.     if (eventPtr->type == MotionNotify) {
  924.     eventPtr->xmotion.same_screen = sameScreen;
  925.     } else {
  926.     eventPtr->xbutton.same_screen = sameScreen;
  927.     }
  928. }
  929.  
  930. /*
  931.  *----------------------------------------------------------------------
  932.  *
  933.  * TkInOutEvents --
  934.  *
  935.  *    This procedure synthesizes EnterNotify and LeaveNotify events
  936.  *    to correctly transfer the pointer from one window to another.
  937.  *    It can also be used to generate FocusIn and FocusOut events
  938.  *    to move the input focus.
  939.  *
  940.  * Results:
  941.  *    None.
  942.  *
  943.  * Side effects:
  944.  *    Synthesized events may be pushed back onto the event queue.
  945.  *    The event pointed to by eventPtr is modified.
  946.  *
  947.  *----------------------------------------------------------------------
  948.  */
  949.  
  950. void
  951. TkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType, position)
  952.     XEvent *eventPtr;        /* A template X event.  Must have all fields
  953.                  * properly set except for type, window,
  954.                  * subwindow, x, y, detail, and same_screen
  955.                  * (Not all of these fields are valid for
  956.                  * FocusIn/FocusOut events;  x_root and y_root
  957.                  * must be valid for Enter/Leave events, even
  958.                  * though x and y needn't be valid). */
  959.     TkWindow *sourcePtr;    /* Window that used to have the pointer or
  960.                  * focus (NULL means it was not in a window
  961.                  * managed by this process). */
  962.     TkWindow *destPtr;        /* Window that is to end up with the pointer
  963.                  * or focus (NULL means it's not one managed
  964.                  * by this process). */
  965.     int leaveType;        /* Type of events to generate for windows
  966.                  * being left (LeaveNotify or FocusOut).  0
  967.                  * means don't generate leave events. */
  968.     int enterType;        /* Type of events to generate for windows
  969.                  * being entered (EnterNotify or FocusIn).  0
  970.                  * means don't generate enter events. */
  971.     Tcl_QueuePosition position;    /* Position at which events are added to
  972.                  * the system event queue. */
  973. {
  974.     register TkWindow *winPtr;
  975.     int upLevels, downLevels, i, j, focus;
  976.  
  977.     /*
  978.      * There are four possible cases to deal with:
  979.      *
  980.      * 1. SourcePtr and destPtr are the same.  There's nothing to do in
  981.      *    this case.
  982.      * 2. SourcePtr is an ancestor of destPtr in the same top-level
  983.      *    window.  Must generate events down the window tree from source
  984.      *    to dest.
  985.      * 3. DestPtr is an ancestor of sourcePtr in the same top-level
  986.      *    window.  Must generate events up the window tree from sourcePtr
  987.      *    to destPtr.
  988.      * 4. All other cases.  Must first generate events up the window tree
  989.      *    from sourcePtr to its top-level, then down from destPtr's
  990.      *    top-level to destPtr. This form is called "non-linear."
  991.      *
  992.      * The call to FindCommonAncestor separates these four cases and decides
  993.      * how many levels up and down events have to be generated for.
  994.      */
  995.  
  996.     if (sourcePtr == destPtr) {
  997.     return;
  998.     }
  999.     if ((leaveType == FocusOut) || (enterType == FocusIn)) {
  1000.     focus = 1;
  1001.     } else {
  1002.     focus = 0;
  1003.     }
  1004.     FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels);
  1005.  
  1006.     /*
  1007.      * Generate enter/leave events and add them to the grab event queue.
  1008.      */
  1009.  
  1010.  
  1011. #define QUEUE(w, t, d)                    \
  1012.     if (w->window != None) {                \
  1013.     eventPtr->type = t;                \
  1014.     if (focus) {                    \
  1015.         eventPtr->xfocus.window = w->window;    \
  1016.         eventPtr->xfocus.detail = d;        \
  1017.     } else {                    \
  1018.         eventPtr->xcrossing.detail = d;        \
  1019.         TkChangeEventWindow(eventPtr, w);        \
  1020.     }                        \
  1021.     Tk_QueueWindowEvent(eventPtr, position);    \
  1022.     }
  1023.  
  1024.     if (downLevels == 0) {
  1025.     
  1026.     /*
  1027.      * SourcePtr is an inferior of destPtr.
  1028.      */
  1029.  
  1030.     if (leaveType != 0) {
  1031.         QUEUE(sourcePtr, leaveType, NotifyAncestor);
  1032.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  1033.             winPtr = winPtr->parentPtr, i--) {
  1034.         QUEUE(winPtr, leaveType, NotifyVirtual);
  1035.         }
  1036.     }
  1037.     if ((enterType != 0) && (destPtr != NULL)) {
  1038.         QUEUE(destPtr, enterType, NotifyInferior);
  1039.     }
  1040.     } else if (upLevels == 0) {
  1041.  
  1042.     /*
  1043.      * DestPtr is an inferior of sourcePtr.
  1044.      */
  1045.  
  1046.     if ((leaveType != 0) && (sourcePtr != NULL)) {
  1047.         QUEUE(sourcePtr, leaveType, NotifyInferior);
  1048.     }
  1049.     if (enterType != 0) {
  1050.         for (i = downLevels-1; i > 0; i--) {
  1051.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  1052.             winPtr = winPtr->parentPtr, j++) {
  1053.         }
  1054.         QUEUE(winPtr, enterType, NotifyVirtual);
  1055.         }
  1056.         if (destPtr != NULL) {
  1057.         QUEUE(destPtr, enterType, NotifyAncestor);
  1058.         }
  1059.     }
  1060.     } else {
  1061.  
  1062.     /*
  1063.      * Non-linear:  neither window is an inferior of the other.
  1064.      */
  1065.  
  1066.     if (leaveType != 0) {
  1067.         QUEUE(sourcePtr, leaveType, NotifyNonlinear);
  1068.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  1069.             winPtr = winPtr->parentPtr, i--) {
  1070.         QUEUE(winPtr, leaveType, NotifyNonlinearVirtual);
  1071.         }
  1072.     }
  1073.     if (enterType != 0) {
  1074.         for (i = downLevels-1; i > 0; i--) {
  1075.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  1076.             winPtr = winPtr->parentPtr, j++) {
  1077.         }
  1078.         QUEUE(winPtr, enterType, NotifyNonlinearVirtual);
  1079.         }
  1080.         if (destPtr != NULL) {
  1081.         QUEUE(destPtr, enterType, NotifyNonlinear);
  1082.         }
  1083.     }
  1084.     }
  1085. }
  1086.  
  1087. /*
  1088.  *----------------------------------------------------------------------
  1089.  *
  1090.  * MovePointer2 --
  1091.  *
  1092.  *    This procedure synthesizes  EnterNotify and LeaveNotify events
  1093.  *    to correctly transfer the pointer from one window to another.
  1094.  *    It is different from TkInOutEvents in that no template X event
  1095.  *    needs to be supplied;  this procedure generates the template
  1096.  *    event and calls TkInOutEvents.
  1097.  *
  1098.  * Results:
  1099.  *    None.
  1100.  *
  1101.  * Side effects:
  1102.  *    Synthesized events may be pushed back onto the event queue.
  1103.  *
  1104.  *----------------------------------------------------------------------
  1105.  */
  1106.  
  1107. static void
  1108. MovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents)
  1109.     TkWindow *sourcePtr;    /* Window currently containing pointer (NULL
  1110.                  * means it's not one managed by this
  1111.                  * process). */
  1112.     TkWindow *destPtr;        /* Window that is to end up containing the
  1113.                  * pointer (NULL means it's not one managed
  1114.                  * by this process). */
  1115.     int mode;            /* Mode for enter/leave events, such as
  1116.                  * NotifyNormal or NotifyUngrab. */
  1117.     int leaveEvents;        /* Non-zero means generate leave events for the
  1118.                  * windows being left.  Zero means don't
  1119.                  * generate leave events. */
  1120.     int enterEvents;        /* Non-zero means generate enter events for the
  1121.                  * windows being entered.  Zero means don't
  1122.                  * generate enter events. */
  1123. {
  1124.     XEvent event;
  1125.     Window dummy1, dummy2;
  1126.     int dummy3, dummy4;
  1127.     TkWindow *winPtr;
  1128.  
  1129.     winPtr = sourcePtr;
  1130.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1131.     winPtr = destPtr;
  1132.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1133.         return;
  1134.     }
  1135.     }
  1136.  
  1137.     event.xcrossing.serial = LastKnownRequestProcessed(
  1138.     winPtr->display);
  1139.     event.xcrossing.send_event = GENERATED_EVENT_MAGIC;
  1140.     event.xcrossing.display = winPtr->display;
  1141.     event.xcrossing.root = RootWindow(winPtr->display,
  1142.         winPtr->screenNum);
  1143.     event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
  1144.     XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
  1145.         &event.xcrossing.x_root, &event.xcrossing.y_root,
  1146.         &dummy3, &dummy4, &event.xcrossing.state);
  1147.     event.xcrossing.mode = mode;
  1148.     event.xcrossing.focus = False;
  1149.     TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0,
  1150.         (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK);
  1151. }
  1152.  
  1153. /*
  1154.  *----------------------------------------------------------------------
  1155.  *
  1156.  * TkGrabDeadWindow --
  1157.  *
  1158.  *    This procedure is invoked whenever a window is deleted, so that
  1159.  *    grab-related cleanup can be performed.
  1160.  *
  1161.  * Results:
  1162.  *    None.
  1163.  *
  1164.  * Side effects:
  1165.  *    Various cleanups happen, such as generating events to move the
  1166.  *    pointer back to its "natural" window as if an ungrab had been
  1167.  *    done.  See the code.
  1168.  *
  1169.  *----------------------------------------------------------------------
  1170.  */
  1171.  
  1172. void
  1173. TkGrabDeadWindow(winPtr)
  1174.     register TkWindow *winPtr;        /* Window that is in the process
  1175.                      * of being deleted. */
  1176. {
  1177.     TkDisplay *dispPtr = winPtr->dispPtr;
  1178.  
  1179.     if (dispPtr->eventualGrabWinPtr == winPtr) {
  1180.     /*
  1181.      * Grab window was deleted.  Release the grab.
  1182.      */
  1183.  
  1184.     Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
  1185.     } else if (dispPtr->buttonWinPtr == winPtr) {
  1186.     ReleaseButtonGrab(dispPtr);
  1187.     }
  1188.     if (dispPtr->serverWinPtr == winPtr) {
  1189.     if (winPtr->flags & TK_TOP_LEVEL) {
  1190.         dispPtr->serverWinPtr = NULL;
  1191.     } else {
  1192.         dispPtr->serverWinPtr = winPtr->parentPtr;
  1193.     }
  1194.     }
  1195.     if (dispPtr->grabWinPtr == winPtr) {
  1196.     dispPtr->grabWinPtr = NULL;
  1197.     }
  1198. }
  1199.  
  1200. /*
  1201.  *----------------------------------------------------------------------
  1202.  *
  1203.  * EatGrabEvents --
  1204.  *
  1205.  *    This procedure is called to eliminate any Enter, Leave,
  1206.  *    FocusIn, or FocusOut events in the event queue for a
  1207.  *    display that have mode NotifyGrab or NotifyUngrab and
  1208.  *    have a serial number no less than a given value and are not
  1209.  *    generated by the grab module.
  1210.  *
  1211.  * Results:
  1212.  *    None.
  1213.  *
  1214.  * Side effects:
  1215.  *    DispPtr's display gets sync-ed, and some of the events get
  1216.  *    removed from the Tk event queue.
  1217.  *
  1218.  *----------------------------------------------------------------------
  1219.  */
  1220.  
  1221. static void
  1222. EatGrabEvents(dispPtr, serial)
  1223.     TkDisplay *dispPtr;        /* Display from which to consume events. */
  1224.     unsigned int serial;    /* Only discard events that have a serial
  1225.                  * number at least this great. */
  1226. {
  1227.     Tk_RestrictProc *oldProc;
  1228.     GrabInfo info;
  1229.     ClientData oldArg, dummy;
  1230.  
  1231.     info.display = dispPtr->display;
  1232.     info.serial = serial;
  1233.     XSync(dispPtr->display, False);
  1234.     oldProc = Tk_RestrictEvents(GrabRestrictProc, (ClientData)&info, &oldArg);
  1235.     while (Tcl_DoOneEvent(TCL_DONT_WAIT|TCL_WINDOW_EVENTS)) {
  1236.     }
  1237.     Tk_RestrictEvents(oldProc, oldArg, &dummy);
  1238. }
  1239.  
  1240. /*
  1241.  *----------------------------------------------------------------------
  1242.  *
  1243.  * GrabRestrictProc --
  1244.  *
  1245.  *    A Tk_RestrictProc used by EatGrabEvents to eliminate any
  1246.  *    Enter, Leave, FocusIn, or FocusOut events in the event queue
  1247.  *    for a display that has mode NotifyGrab or NotifyUngrab and
  1248.  *    have a serial number no less than a given value.
  1249.  *
  1250.  * Results:
  1251.  *    Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
  1252.  *
  1253.  * Side effects:
  1254.  *    None.
  1255.  *
  1256.  *----------------------------------------------------------------------
  1257.  */
  1258.  
  1259. static Tk_RestrictAction
  1260. GrabRestrictProc(arg, eventPtr)
  1261.     ClientData arg;
  1262.     XEvent *eventPtr;
  1263. {
  1264.     GrabInfo *info = (GrabInfo *) arg;
  1265.     int mode, diff;
  1266.  
  1267.     /*
  1268.      * The diff caculation is trickier than it may seem.  Don't forget
  1269.      * that serial numbers can wrap around, so can't compare the two
  1270.      * serial numbers directly.
  1271.      */
  1272.  
  1273.     diff = eventPtr->xany.serial - info->serial;
  1274.     if ((eventPtr->type == EnterNotify)
  1275.         || (eventPtr->type == LeaveNotify)) {
  1276.     mode = eventPtr->xcrossing.mode;
  1277.     } else if ((eventPtr->type == FocusIn)
  1278.         || (eventPtr->type == FocusOut)) {
  1279.     mode = eventPtr->xfocus.mode;
  1280.     } else {
  1281.     mode = NotifyNormal;
  1282.     }
  1283.     if ((info->display != eventPtr->xany.display) || (mode == NotifyNormal)
  1284.         || (diff < 0)) {
  1285.     return TK_DEFER_EVENT;
  1286.     } else {
  1287.     return TK_DISCARD_EVENT;
  1288.     }
  1289. }
  1290.  
  1291. /*
  1292.  *----------------------------------------------------------------------
  1293.  *
  1294.  * QueueGrabWindowChange --
  1295.  *
  1296.  *    This procedure queues a special event in the Tcl event queue,
  1297.  *    which will cause the "grabWinPtr" field for the display to get
  1298.  *    modified when the event is processed.  This is needed to make
  1299.  *    sure that the grab window changes at the proper time relative
  1300.  *    to grab-related enter and leave events that are also in the
  1301.  *    queue.  In particular, this approach works even when multiple
  1302.  *    grabs and ungrabs happen back-to-back.
  1303.  *
  1304.  * Results:
  1305.  *    None.
  1306.  *
  1307.  * Side effects:
  1308.  *    DispPtr->grabWinPtr will be modified later (by GrabWinEventProc)
  1309.  *    when the event is removed from the grab event queue.
  1310.  *
  1311.  *----------------------------------------------------------------------
  1312.  */
  1313.  
  1314. static void
  1315. QueueGrabWindowChange(dispPtr, grabWinPtr)
  1316.     TkDisplay *dispPtr;        /* Display on which to change the grab
  1317.                  * window. */
  1318.     TkWindow *grabWinPtr;    /* Window that is to become the new grab
  1319.                  * window (may be NULL). */
  1320. {
  1321.     NewGrabWinEvent *grabEvPtr;
  1322.  
  1323.     grabEvPtr = (NewGrabWinEvent *) ckalloc(sizeof(NewGrabWinEvent));
  1324.     grabEvPtr->header.proc = GrabWinEventProc;
  1325.     grabEvPtr->dispPtr = dispPtr;
  1326.     if (grabWinPtr == NULL) {
  1327.     grabEvPtr->grabWindow = None;
  1328.     } else {
  1329.     grabEvPtr->grabWindow = grabWinPtr->window;
  1330.     }
  1331.     Tcl_QueueEvent(&grabEvPtr->header, TCL_QUEUE_MARK);
  1332.     dispPtr->eventualGrabWinPtr = grabWinPtr;
  1333. }
  1334.  
  1335. /*
  1336.  *----------------------------------------------------------------------
  1337.  *
  1338.  * GrabWinEventProc --
  1339.  *
  1340.  *    This procedure is invoked as a handler for Tcl_Events of type
  1341.  *    NewGrabWinEvent.  It updates the current grab window field in
  1342.  *    a display.
  1343.  *
  1344.  * Results:
  1345.  *    Returns 1 if the event was processed, 0 if it should be deferred
  1346.  *    for processing later.
  1347.  *
  1348.  * Side effects:
  1349.  *    The grabWinPtr field is modified in the display associated with
  1350.  *    the event.
  1351.  *
  1352.  *----------------------------------------------------------------------
  1353.  */
  1354.  
  1355. static int
  1356. GrabWinEventProc(evPtr, flags)
  1357.     Tcl_Event *evPtr;        /* Event of type NewGrabWinEvent. */
  1358.     int flags;            /* Flags argument to Tk_DoOneEvent: indicates
  1359.                  * what kinds of events are being processed
  1360.                  * right now. */
  1361. {
  1362.     NewGrabWinEvent *grabEvPtr = (NewGrabWinEvent *) evPtr;
  1363.  
  1364.     grabEvPtr->dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow(
  1365.         grabEvPtr->dispPtr->display, grabEvPtr->grabWindow);
  1366.     return 1;
  1367. }
  1368.  
  1369. /*
  1370.  *----------------------------------------------------------------------
  1371.  *
  1372.  * FindCommonAncestor --
  1373.  *
  1374.  *    Given two windows, this procedure finds their least common
  1375.  *    ancestor and also computes how many levels up this ancestor
  1376.  *    is from each of the original windows.
  1377.  *
  1378.  * Results:
  1379.  *    If the windows are in different applications or top-level
  1380.  *    windows, then NULL is returned and *countPtr1 and *countPtr2
  1381.  *    are set to the depths of the two windows in their respective
  1382.  *    top-level windows (1 means the window is a top-level, 2 means
  1383.  *    its parent is a top-level, and so on).  Otherwise, the return
  1384.  *    value is a pointer to the common ancestor and the counts are
  1385.  *    set to the distance of winPtr1 and winPtr2 from this ancestor
  1386.  *    (1 means they're children, 2 means grand-children, etc.).
  1387.  *
  1388.  * Side effects:
  1389.  *    None.
  1390.  *
  1391.  *----------------------------------------------------------------------
  1392.  */
  1393.  
  1394. static TkWindow *
  1395. FindCommonAncestor(winPtr1, winPtr2, countPtr1, countPtr2)
  1396.     TkWindow *winPtr1;        /* First window.   May be NULL. */
  1397.     TkWindow *winPtr2;        /* Second window.  May be NULL. */
  1398.     int *countPtr1;        /* Store nesting level of winPtr1 within
  1399.                  * common ancestor here. */
  1400.     int *countPtr2;        /* Store nesting level of winPtr2 within
  1401.                  * common ancestor here. */
  1402. {
  1403.     register TkWindow *winPtr;
  1404.     TkWindow *ancestorPtr;
  1405.     int count1, count2, i;
  1406.  
  1407.     /*
  1408.      * Mark winPtr1 and all of its ancestors with a special flag bit.
  1409.      */
  1410.  
  1411.     if (winPtr1 != NULL) {
  1412.     for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) {
  1413.         winPtr->flags |= TK_GRAB_FLAG;
  1414.         if (winPtr->flags & TK_TOP_LEVEL) {
  1415.         break;
  1416.         }
  1417.     }
  1418.     }
  1419.  
  1420.     /*
  1421.      * Search upwards from winPtr2 until an ancestor of winPtr1 is
  1422.      * found or a top-level window is reached.
  1423.      */
  1424.  
  1425.     winPtr = winPtr2;
  1426.     count2 = 0;
  1427.     ancestorPtr = NULL;
  1428.     if (winPtr2 != NULL) {
  1429.     for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) {
  1430.         if (winPtr->flags & TK_GRAB_FLAG) {
  1431.         ancestorPtr = winPtr;
  1432.         break;
  1433.         }
  1434.         if (winPtr->flags & TK_TOP_LEVEL)  {
  1435.         count2++;
  1436.         break;
  1437.         }
  1438.     }
  1439.     }
  1440.  
  1441.     /*
  1442.      * Search upwards from winPtr1 again, clearing the flag bits and
  1443.      * remembering how many levels up we had to go.
  1444.      */
  1445.  
  1446.     if (winPtr1 == NULL) {
  1447.     count1 = 0;
  1448.     } else {
  1449.     count1 = -1;
  1450.     for (i = 0, winPtr = winPtr1; winPtr != NULL;
  1451.         i++, winPtr = winPtr->parentPtr) {
  1452.         winPtr->flags &= ~TK_GRAB_FLAG;
  1453.         if (winPtr == ancestorPtr) {
  1454.         count1 = i;
  1455.         }
  1456.         if (winPtr->flags & TK_TOP_LEVEL) {
  1457.         if (count1 == -1) {
  1458.             count1 = i+1;
  1459.         }
  1460.         break;
  1461.         }
  1462.     }
  1463.     }
  1464.  
  1465.     *countPtr1 = count1;
  1466.     *countPtr2 = count2;
  1467.     return ancestorPtr;
  1468. }
  1469.  
  1470. /*
  1471.  *----------------------------------------------------------------------
  1472.  *
  1473.  * TkPositionInTree --
  1474.  *
  1475.  *    Compute where the given window is relative to a particular
  1476.  *    subtree of the window hierarchy.
  1477.  *
  1478.  * Results:
  1479.  *
  1480.  *    Returns TK_GRAB_IN_TREE if the window is contained in the
  1481.  *    subtree.  Returns TK_GRAB_ANCESTOR if the window is an
  1482.  *    ancestor of the subtree, in the same toplevel.  Otherwise
  1483.  *    it returns TK_GRAB_EXCLUDED.
  1484.  *
  1485.  * Side effects:
  1486.  *    None.
  1487.  *
  1488.  *----------------------------------------------------------------------
  1489.  */
  1490.  
  1491. int
  1492. TkPositionInTree(winPtr, treePtr)
  1493.     TkWindow *winPtr;        /* Window to be checked. */
  1494.     TkWindow *treePtr;        /* Root of tree to compare against. */
  1495. {
  1496.     TkWindow *winPtr2;
  1497.  
  1498.     for (winPtr2 = winPtr; winPtr2 != treePtr;
  1499.        winPtr2 = winPtr2->parentPtr) {
  1500.     if (winPtr2 == NULL) {
  1501.         for (winPtr2 = treePtr; winPtr2 != NULL;
  1502.             winPtr2 = winPtr2->parentPtr) {
  1503.         if (winPtr2 == winPtr) {
  1504.             return TK_GRAB_ANCESTOR;
  1505.         }
  1506.         if (winPtr2->flags & TK_TOP_LEVEL) {
  1507.             break;
  1508.         }
  1509.         }
  1510.         return TK_GRAB_EXCLUDED;
  1511.     }
  1512.     }
  1513.     return TK_GRAB_IN_TREE;
  1514. }
  1515.  
  1516. /*
  1517.  *----------------------------------------------------------------------
  1518.  *
  1519.  * TkGrabState --
  1520.  *
  1521.  *    Given a window, this procedure returns a value that indicates
  1522.  *    the grab state of the application relative to the window.
  1523.  *
  1524.  * Results:
  1525.  *    The return value is one of three things:
  1526.  *        TK_GRAB_NONE -    no grab is in effect.
  1527.  *        TK_GRAB_IN_TREE -   there is a grab in effect, and winPtr
  1528.  *                is in the grabbed subtree.
  1529.  *        TK_GRAB_ANCESTOR -  there is a grab in effect;  winPtr is
  1530.  *                an ancestor of the grabbed window, in
  1531.  *                the same toplevel.
  1532.  *        TK_GRAB_EXCLUDED -    there is a grab in effect; winPtr is
  1533.  *                outside the tree of the grab and is not
  1534.  *                an ancestor of the grabbed window in the
  1535.  *                same toplevel.
  1536.  *
  1537.  * Side effects:
  1538.  *    None.
  1539.  *
  1540.  *----------------------------------------------------------------------
  1541.  */
  1542.  
  1543. int
  1544. TkGrabState(winPtr)
  1545.     TkWindow *winPtr;        /* Window for which grab information is
  1546.                  * needed. */
  1547. {
  1548.     TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
  1549.  
  1550.     if (grabWinPtr == NULL) {
  1551.     return TK_GRAB_NONE;
  1552.     }
  1553.     if ((winPtr->mainPtr != grabWinPtr->mainPtr)
  1554.         && !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) {
  1555.     return TK_GRAB_NONE;
  1556.     }
  1557.  
  1558.     return TkPositionInTree(winPtr, grabWinPtr);
  1559. }
  1560.